/*
 * Copyright (c) 2025 Industrial Software Feature Database
 */
package com.comac.ins.isfd.service.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import com.comac.ins.isfd.domain.vo.IsfdModelVo;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.comac.ins.common.core.exception.ServiceException;
import com.comac.ins.common.core.utils.MapstructUtils;
import com.comac.ins.common.core.utils.StreamUtils;
import com.comac.ins.common.core.utils.StringUtils;
import com.comac.ins.common.core.utils.file.FileUtils;
import com.comac.ins.common.mybatis.core.page.PageQuery;
import com.comac.ins.common.mybatis.core.page.TableDataInfo;
import com.comac.ins.common.oss.core.OssClient;
import com.comac.ins.common.oss.entity.UploadResult;
import com.comac.ins.common.oss.enumd.AccessPolicyType;
import com.comac.ins.common.oss.factory.OssFactory;
import com.comac.ins.isfd.domain.IsfdCalculationExample;
import com.comac.ins.isfd.domain.bo.IsfdCalculationExampleBo;
import com.comac.ins.isfd.domain.vo.CalculationExampleCountObjectVo;
import com.comac.ins.isfd.domain.vo.CalculationExampleCountResultObjectVo;
import com.comac.ins.isfd.domain.vo.IsfdCalculationExampleVo;
import com.comac.ins.isfd.mapper.CalculationExampleMapper;
import com.comac.ins.isfd.service.IIsfdCalculationExampleService;
import com.comac.ins.isfd.util.DateUtils;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;

/**
 * 算例库service层接口实现层
 *
 * @author zhouyaoixn
 * @date 2024-7-17
 */
@RequiredArgsConstructor
@Service
public class IsfdCalculationExampleServiceImpl implements IIsfdCalculationExampleService {

    private final CalculationExampleMapper calculationExampleMapper;
    @Value("${evn.evnType}")
    private String evnType;

    @Override
    public List<CalculationExampleCountObjectVo> listCount() {
        List<CalculationExampleCountObjectVo> calculationExample = new ArrayList<>();
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.select("calculation_example_type", "COUNT(*) as total_count");
        queryWrapper.groupBy("calculation_example_type");
        List<Map<String, Object>> list = calculationExampleMapper.selectMaps(queryWrapper);
        if (CollectionUtils.isNotEmpty(list)) {
            list.forEach((m -> {
                calculationExample.add(new CalculationExampleCountObjectVo((String) m.get("calculation_example_type"), (Long) m.get("total_count")));
            }));
        }
        return calculationExample;
    }

    @Override
    public List<CalculationExampleCountResultObjectVo> listCountByListByField(String field) {
        List<CalculationExampleCountResultObjectVo> calculationExample = new ArrayList<>();
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.select(field, "COUNT(*) as total_count");
        queryWrapper.groupBy(field);
        List<Map<String, Object>> list = calculationExampleMapper.selectMaps(queryWrapper);
        if (CollectionUtils.isNotEmpty(list)) {
            list.forEach((m -> {
                calculationExample.add(new CalculationExampleCountResultObjectVo(null, (String) m.get(field), (Long) m.get("total_count")));
            }));
        }
        return calculationExample;
    }

    @Override
    public List<CalculationExampleCountResultObjectVo> listTimeCount(String field, Date startTime, Date endTime) {
        List<CalculationExampleCountResultObjectVo> result = new ArrayList<>();
        QueryWrapper queryWrapper = new QueryWrapper<>();

        // 判断 field 是否为空，如果为空则不按该字段分组
        if (StringUtils.isEmpty(field)) {
            queryWrapper.select("DATE_FORMAT(create_time, '%Y-%m') as month", "COUNT(*) as total_count");
            queryWrapper.groupBy("month");
        } else {
            queryWrapper.select("DATE_FORMAT(create_time, '%Y-%m') as month", field + " as field_value", "COUNT(*) as total_count");
            queryWrapper.groupBy("month", "field_value");
        }

        // 时间条件
        if (Objects.nonNull(startTime) && Objects.nonNull(endTime)) {
            queryWrapper.between("create_time", startTime, endTime);
        } else if (Objects.nonNull(startTime)) {
            queryWrapper.ge("create_time", startTime);
        } else if (Objects.nonNull(endTime)) {
            queryWrapper.le("create_time", endTime);
        }

        List<Map<String, Object>> list = calculationExampleMapper.selectMaps(queryWrapper);

        // 获取指定时间范围内的所有月份
        List<String> allMonths = DateUtils.getAllMonthsBetween(startTime, endTime);
        Set<String> fieldValues = list.stream()
            .filter(m -> m.get("field_value") != null)
            .map(m -> (String) m.get("field_value"))
            .collect(Collectors.toSet());

        // 如果没有指定 field，只用月份显示
        if (StringUtils.isEmpty(field)) {
            allMonths.forEach(month -> {
                Long count = list.stream()
                    .filter(m -> month.equals(m.get("month")))
                    .map(m -> (Long) m.get("total_count"))
                    .findFirst()
                    .orElse(0L);
                result.add(new CalculationExampleCountResultObjectVo(month, "", count));
            });
        } else {
            // 对每个月和每个字段值进行组合，确保没有数据的组合也显示 totalCount=0
            for (String month : allMonths) {
                for (String fieldValue : fieldValues) {
                    Long count = list.stream()
                        .filter(m -> month.equals(m.get("month")) && fieldValue.equals(m.get("field_value")))
                        .map(m -> (Long) m.get("total_count"))
                        .findFirst()
                        .orElse(0L);
                    result.add(new CalculationExampleCountResultObjectVo(month, fieldValue, count));
                }
            }
        }
        return result;
    }


    @Override
    public IsfdCalculationExampleVo getById(Long calculationExampleId) {
        return calculationExampleMapper.selectVoById(calculationExampleId);
    }

    @Override
    public TableDataInfo<IsfdCalculationExampleVo> queryPageList(IsfdCalculationExampleBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<IsfdCalculationExample> lqw = buildQueryWrapper(bo, pageQuery);
        Page<IsfdCalculationExampleVo> calculationExample = calculationExampleMapper.selectVoPage(pageQuery.build(), lqw);
//        List<IsfdCalculationExampleVo> filterCalculationExample = StreamUtils.toList(calculationExample.getRecords(), this::matchingUrl);
        List<IsfdCalculationExampleVo> calculationExampleResult = new ArrayList<>();
        if(evnType.equals("red")){
            calculationExampleResult = StreamUtils.toList(calculationExample.getRecords(), this::SignUrl);
        }else{
            calculationExampleResult = StreamUtils.toList(calculationExample.getRecords(), this::matchingUrl);
        }

        calculationExample.setRecords(calculationExampleResult);
        return TableDataInfo.build(calculationExample);
    }
    private IsfdCalculationExampleVo SignUrl (IsfdCalculationExampleVo modelVo){
        OssClient storage = OssFactory.instance(modelVo.getService());
        modelVo.setUrl(storage.OssSignedUrl(modelVo.getFileName()));
        return modelVo;
    }
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            // todo
            // 做一些业务上的校验,判断是否需要校验
        }
        List<IsfdCalculationExample> list = calculationExampleMapper.selectBatchIds(ids);
        for (IsfdCalculationExample isfdCalculationExample : list) {
            OssClient storage = OssFactory.instance(isfdCalculationExample.getService());
            storage.delete(isfdCalculationExample.getUrl());
        }
        return calculationExampleMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public IsfdCalculationExampleVo save(MultipartFile file, IsfdCalculationExampleBo isfdCalculationExampleBo) {
        String originalFileName = file.getOriginalFilename();
        String suffix = StringUtils.substring(originalFileName, originalFileName.lastIndexOf("."), originalFileName.length());
        OssClient storage = OssFactory.instance();
        UploadResult uploadResult;
        try {
            uploadResult = storage.uploadSuffix(file.getBytes(), suffix);
        } catch (IOException e) {
            throw new ServiceException(e.getMessage());
        }
        // 保存文件信息
        return saveCalculationExampleDetail(originalFileName, storage.getConfigKey(), uploadResult, isfdCalculationExampleBo);
    }

    @NotNull
    private IsfdCalculationExampleVo saveCalculationExampleDetail(String originalfileName, String configKey,
                                                                  UploadResult uploadResult, IsfdCalculationExampleBo isfdCalculationExampleBo) {
        LambdaQueryWrapper<IsfdCalculationExample> qr = new LambdaQueryWrapper<>();
        qr.eq(IsfdCalculationExample::getDisplayName, isfdCalculationExampleBo.getDisplayName());
        IsfdCalculationExample ie = calculationExampleMapper.selectOne(qr);
        if (ie != null) {
            throw new RuntimeException("算例" + isfdCalculationExampleBo.getDisplayName() + "的算例文档已存在");
        }
        IsfdCalculationExample isfdCalculationExample = new IsfdCalculationExample();
        isfdCalculationExample.setUrl(uploadResult.getUrl());
        isfdCalculationExample.setFileName(uploadResult.getFilename());
        isfdCalculationExample.setOriginalName(originalfileName);
        isfdCalculationExample.setService(configKey);
        isfdCalculationExample.setDisplayName(isfdCalculationExampleBo.getDisplayName());
        isfdCalculationExample.setCalculationExampleType(isfdCalculationExampleBo.getCalculationExampleType());
        isfdCalculationExample.setAnalysisType(isfdCalculationExampleBo.getAnalysisType());
        calculationExampleMapper.insert(isfdCalculationExample);
        IsfdCalculationExampleVo isfdCalculationExampleVo = MapstructUtils.convert(isfdCalculationExample, IsfdCalculationExampleVo.class);
        return this.matchingUrl(isfdCalculationExampleVo);
    }

    @Override
    public void download(Long calculationExampleId, HttpServletResponse response) throws IOException {
        IsfdCalculationExampleVo isfdCalculationExampleVo = this.getById(calculationExampleId);
        if (ObjectUtil.isNull(isfdCalculationExampleVo)) {
            throw new ServiceException("文件数据不存在!");
        }
        FileUtils.setAttachmentResponseHeader(response, isfdCalculationExampleVo.getOriginalName());
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
        OssClient storage = OssFactory.instance(isfdCalculationExampleVo.getService());
        long contentLength = storage.download(isfdCalculationExampleVo.getFileName(), response.getOutputStream());
        response.setContentLengthLong(contentLength);
    }

    @Override
    public void update(IsfdCalculationExampleBo isfdCalculationExampleBo) {
        LambdaQueryWrapper<IsfdCalculationExample> qr = new LambdaQueryWrapper<>();
        qr.eq(IsfdCalculationExample::getDisplayName, isfdCalculationExampleBo.getDisplayName());
        IsfdCalculationExample ie = calculationExampleMapper.selectOne(qr);
        if (ie != null) {
            throw new RuntimeException("算例" + isfdCalculationExampleBo.getDisplayName() + "的算例文档已存在");
        }
        LambdaUpdateWrapper<IsfdCalculationExample> updateWrapper = new LambdaUpdateWrapper();
        updateWrapper.eq(IsfdCalculationExample::getCalculationExampleId, isfdCalculationExampleBo.getCalculationExampleId());
        updateWrapper.set(IsfdCalculationExample::getDisplayName, isfdCalculationExampleBo.getDisplayName());
        updateWrapper.set(IsfdCalculationExample::getAnalysisType, isfdCalculationExampleBo.getAnalysisType());
        calculationExampleMapper.update(updateWrapper);

    }

    @Override
    public Boolean checkIfExistCalculationExample(String analysisType) {
        LambdaQueryWrapper<IsfdCalculationExample> qr = new LambdaQueryWrapper<>();
        qr.eq(IsfdCalculationExample::getAnalysisType, analysisType);
        Long count = calculationExampleMapper.selectCount(qr);
        if (count > 0) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }


    private LambdaQueryWrapper<IsfdCalculationExample> buildQueryWrapper(IsfdCalculationExampleBo bo, PageQuery pageQuery) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<IsfdCalculationExample> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getDisplayName()), IsfdCalculationExample::getDisplayName, bo.getDisplayName());
        lqw.like(StringUtils.isNotBlank(bo.getCalculationExampleType()), IsfdCalculationExample::getCalculationExampleType, bo.getCalculationExampleType());
        lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), IsfdCalculationExample::getOriginalName, bo.getOriginalName());
        lqw.like(StringUtils.isNotBlank(bo.getAnalysisType()), IsfdCalculationExample::getAnalysisType, bo.getAnalysisType());
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
            IsfdCalculationExample::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
        return lqw;
    }


    private IsfdCalculationExampleVo matchingUrl(IsfdCalculationExampleVo calculationExampleVo) {
        OssClient storage = OssFactory.instance(calculationExampleVo.getService());
        // 仅修改桶类型为 private 的URL，临时URL时长为120s
        if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
            calculationExampleVo.setUrl(storage.getPrivateUrl(calculationExampleVo.getFileName(), 120));
        }
        return calculationExampleVo;
    }
}
